#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <unistd.h>

#define MAXLINE       200

static struct cmsghdr *cmptr = NULL;


int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t)) {
    int newfd, nr, status;
    char *ptr;
    char buf[MAXLINE];
    struct iovec iov[1];
    struct msghdr msg;

    status = -1;
    for (; ;) {
        iov[0].iov_base = buf;
        iov[0].iov_len = sizeof(buf);
        msg.msg_iov = iov;
        msg.msg_iovlen = 1;
        msg.msg_name = NULL;
        msg.msg_namelen = 0;

        if (cmptr == NULL) {
            cmptr = (struct cmsghdr *)malloc(CMSG_LEN(sizeof(int)));
            if (cmptr == NULL) 
                return -1;
        }

        msg.msg_control = cmptr;
        msg.msg_controllen = CMSG_LEN(sizeof(int));

        nr = recvmsg(fd, &msg, 0);
        if (nr < 0) {
            perror("recvmsg error");
            return -1;
        }
        else if (nr == 0) {
            perror("connection closd by server");
            return -1;
        }

        for (ptr = buf; ptr < &buf[nr]; ) {
            if (*ptr++ == 0) {
                if (ptr != &buf[nr - 1]) {
                    fprintf(stderr, "message format error");
                    exit(1);
                }

                status = *ptr & 0xFF;
                if (status == 0) {
                    if (msg.msg_controllen < CMSG_LEN(sizeof(int))) {
                        fprintf(stderr, "status = 0 buf no fd");
                        newfd = *(int *)CMSG_DATA(cmptr);
                    }
                }
                else {
                    newfd = -status;
                }

                    nr -= 2;
            }
        }

        if (nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr)
            return -1;

        if (status >= 0)
            return newfd;
    }
}

